package distributedlock.utils;

public class Snowflake {
    /**
     * 默认起始的时间戳
     */
    private static final long DFT_EPOCH = 1609430400000L;

    /**
     * 每一部分占用的位数
     */
    private static final long WORKER_ID_BITS = 5L;
    private static final long DATA_CENTER_ID_BITS = 5L;
    private static final long SEQUENCE_BITS = 12L;

    /**
     * 每一部分的最大值
     */
    private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
    private static final long MAX_DATACENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);

    /**
     * 每一部分向左的位移
     */
    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
    private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    private static final long TIMESTAMP_SHIFT =
      SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;

    private final long dataCenterId; // 数据中心ID
    private final long workerId; // 机器ID
    private final long epoch; // 起始时间
    private long sequence = 0L; // 序列号
    private long lastTimestamp = -1L; // 上一次时间戳

    public Snowflake(long workerId, long dataCenterId) {
        this(workerId, dataCenterId, DFT_EPOCH);
    }

    public Snowflake(long workerId, long dataCenterId, long epoch) {
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException(
              String.format("worker Id can't be greater than %d or less than 0",
                MAX_WORKER_ID));
        }
        if (dataCenterId > MAX_DATACENTER_ID || dataCenterId < 0) {
            throw new IllegalArgumentException(
              String.format("datacenter Id can't be greater than %d or less than 0",
                MAX_DATACENTER_ID));
        }
        this.workerId = workerId;
        this.dataCenterId = dataCenterId;
        this.epoch = epoch;
    }

    public synchronized String nextIdString() {
        return Long.toString(nextId(), 36).toUpperCase();
    }

    public synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            throw new IllegalStateException(String.format(
              "Clock moved backwards.  Refusing to generate id for %d milliseconds",
              lastTimestamp - timestamp));
        }
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0L) {
                timestamp = tilNextMillis();
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;

        return (timestamp - epoch) << TIMESTAMP_SHIFT // 时间戳部分
          | dataCenterId << DATACENTER_ID_SHIFT // 数据中心部分
          | workerId << WORKER_ID_SHIFT // 机器标识部分
          | sequence; // 序列号部分
    }

    private long tilNextMillis() {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }
}
